#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
触发器对话框模块 - 用于配置触发器
"""

import logging
import json
import os
from typing import Dict, List, Any, Optional
from PyQt5.QtWidgets import (
    QDialog, QVBoxLayout, QHBoxLayout, QLabel, 
    QLineEdit, QPushButton, QFormLayout, QDialogButtonBox,
    QComboBox, QTabWidget, QWidget, QTextEdit,
    QCheckBox, QMessageBox, QTableWidget, QTableWidgetItem,
    QHeaderView, QGroupBox, QRadioButton, QFileDialog,
    QScrollArea, QFrame
)
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtGui import QColor

# 导入Excel处理库
try:
    import pandas as pd
    PANDAS_AVAILABLE = True
except ImportError:
    PANDAS_AVAILABLE = False

from src.zabbix_api import ZabbixAPI

logger = logging.getLogger(__name__)

class TriggerDialog(QDialog):
    """触发器配置对话框"""
    
    def __init__(self, api: ZabbixAPI, hosts: List[Dict[str, Any]], parent=None):
        """
        初始化触发器对话框
        
        Args:
            api (ZabbixAPI): Zabbix API客户端
            hosts (List[Dict[str, Any]]): 主机列表
            parent: 父窗口
        """
        super().__init__(parent)
        self.api = api
        self.hosts = hosts
        
        # 初始化复合表达式列表
        self.expressions = []
        
        # 获取主机的IP地址
        self.host_ips = self.get_host_ip_addresses()
        
        # 预先获取所有主机的完整信息
        self.host_info_map = {}  # 主机ID -> 完整信息
        self.name_to_host_map = {}  # 可见名称 -> 技术名称
        self.load_complete_host_info()
        
        self.setup_ui()
        
        # 初始化函数类型限制
        self.on_function_changed(self.function_combo.currentIndex())
        self.on_recovery_function_changed(self.recovery_function_combo.currentIndex())
    
    def log_message(self, message):
        """记录日志消息，添加异常处理确保日志问题不会导致程序崩溃"""
        try:
            # 将message转换为字符串，防止非字符串类型导致错误
            message_str = str(message)
            logger.info(message_str)
        except Exception as e:
            # 如果日志记录失败，至少尝试打印到控制台
            try:
                print(f"日志记录失败: {str(message)}, 错误: {str(e)}")
            except:
                print("日志记录完全失败，无法打印详情")
    
    def load_complete_host_info(self):
        """预先加载所有主机的完整信息"""
        try:
            # 获取所有主机ID
            hostids = [host.get('hostid') for host in self.hosts if host.get('hostid')]
            
            if not hostids:
                self.log_message("警告：没有找到任何主机ID")
                return
                
            self.log_message(f"正在预先获取 {len(hostids)} 台主机的完整信息...")
            
            try:
                # 直接使用JSON-RPC调用获取完整主机信息
                if hasattr(self.api, 'auth') and hasattr(self.api, 'url'):
                    import requests
                    json_request = {
                        "jsonrpc": "2.0",
                        "method": "host.get",
                        "params": {
                            "hostids": hostids,
                            "output": ["hostid", "host", "name"]
                        },
                        "auth": self.api.auth,
                        "id": 0
                    }
                    
                    url = self.api.url
                    headers = {'Content-Type': 'application/json-rpc'}
                    r = requests.post(url, json=json_request, headers=headers)
                    response = r.json()
                    
                    if "result" in response and response["result"]:
                        host_data = response["result"]
                        self.log_message(f"成功获取 {len(host_data)} 台主机的完整信息")
                        
                        # 建立映射关系
                        for host in host_data:
                            hostid = host.get("hostid")
                            technical_name = host.get("host")
                            visible_name = host.get("name")
                            
                            if hostid:
                                self.host_info_map[hostid] = host
                            
                            if visible_name and technical_name:
                                self.name_to_host_map[visible_name] = technical_name
                                self.log_message(f"映射: 可见名称 '{visible_name}' -> 技术名称 '{technical_name}'")
                        
                        # 打印主机信息以便调试
                        for host in self.hosts:
                            hostid = host.get("hostid")
                            visible_name = host.get("name", "")
                            if hostid in self.host_info_map:
                                technical_name = self.host_info_map[hostid].get("host", "")
                                self.log_message(f"主机信息: ID={hostid}, 技术名称={technical_name}, 可见名称={visible_name}")
                            else:
                                self.log_message(f"警告: 无法找到主机 ID={hostid}, 可见名称={visible_name} 的完整信息")
                # 如果上面的方法失败，尝试使用API的do_request方法
                elif hasattr(self.api, 'do_request'):
                    response = self.api.do_request('host.get', {
                        'hostids': hostids,
                        'output': ["hostid", "host", "name"]
                    })
                    
                    if isinstance(response, dict) and "result" in response and response["result"]:
                        host_data = response["result"]
                        self.log_message(f"成功获取 {len(host_data)} 台主机的完整信息")
                        
                        # 建立映射关系
                        for host in host_data:
                            hostid = host.get("hostid")
                            technical_name = host.get("host")
                            visible_name = host.get("name")
                            
                            if hostid:
                                self.host_info_map[hostid] = host
                            
                            if visible_name and technical_name:
                                self.name_to_host_map[visible_name] = technical_name
                        
                        # 打印主机信息以便调试
                        for host in self.hosts:
                            hostid = host.get("hostid")
                            visible_name = host.get("name", "")
                            if hostid in self.host_info_map:
                                technical_name = self.host_info_map[hostid].get("host", "")
                                self.log_message(f"主机信息: ID={hostid}, 技术名称={technical_name}, 可见名称={visible_name}")
                            else:
                                self.log_message(f"警告: 无法找到主机 ID={hostid}, 可见名称={visible_name} 的完整信息")
            except Exception as api_err:
                self.log_message(f"通过API获取主机完整信息失败: {str(api_err)}")
        except Exception as e:
            self.log_message(f"加载主机完整信息时出错: {str(e)}")
    
    def get_host_ip_addresses(self):
        """获取主机的IP地址"""
        host_ips = {}  # 存储主机ID到IP地址的映射
        
        try:
            # 获取所有主机ID
            hostids = [host.get('hostid') for host in self.hosts if host.get('hostid')]
            
            if not hostids:
                return host_ips
                
            logger.info(f"正在获取 {len(hostids)} 台主机的IP地址...")
            
            # 使用hostinterface.get API获取主机接口
            try:
                # 尝试不同的API调用方式
                if hasattr(self.api, 'hostinterface') and hasattr(self.api.hostinterface, 'get'):
                    interfaces = self.api.hostinterface.get(
                        hostids=hostids,
                        output="extend"
                    )
                    logger.info("使用 api.hostinterface.get() 获取主机接口")
                elif hasattr(self.api, 'do_request'):
                    interfaces_resp = self.api.do_request('hostinterface.get', {
                        'hostids': hostids,
                        'output': "extend"
                    })
                    if isinstance(interfaces_resp, dict) and "result" in interfaces_resp:
                        interfaces = interfaces_resp["result"]
                    else:
                        interfaces = interfaces_resp
                    logger.info("使用 api.do_request() 获取主机接口")
                elif hasattr(self.api, 'auth') and hasattr(self.api, 'url'):
                    # 直接使用JSON-RPC调用
                    import requests
                    json_request = {
                        "jsonrpc": "2.0",
                        "method": "hostinterface.get",
                        "params": {
                            "output": "extend",
                            "hostids": hostids
                        },
                        "auth": self.api.auth,
                        "id": 1
                    }
                    
                    # 尝试使用json_request方法或直接HTTP请求
                    if hasattr(self.api, 'json_request'):
                        response = self.api.json_request(json_request)
                        interfaces = response.get("result", [])
                        logger.info("使用 api.json_request() 获取主机接口")
                    else:
                        url = self.api.url
                        headers = {'Content-Type': 'application/json-rpc'}
                        r = requests.post(url, json=json_request, headers=headers)
                        response = r.json()
                        interfaces = response.get("result", [])
                        logger.info("使用直接HTTP请求获取主机接口")
                else:
                    logger.warning("无法找到合适的方法获取主机接口")
                    return host_ips
                
                # 处理接口数据
                if interfaces:
                    logger.info(f"找到 {len(interfaces)} 个主机接口")
                    for interface in interfaces:
                        hostid = interface.get('hostid')
                        ip = interface.get('ip')
                        if hostid and ip:
                            if hostid not in host_ips:
                                host_ips[hostid] = []
                            host_ips[hostid].append(ip)
                    
                    logger.info(f"处理完成，为 {len(host_ips)} 台主机找到了IP地址")
            except Exception as e:
                logger.error(f"获取主机接口时出错: {str(e)}")
        except Exception as e:
            logger.error(f"获取主机IP地址时出错: {str(e)}")
        
        return host_ips
    
    def setup_ui(self):
        """设置UI界面"""
        self.setWindowTitle("创建触发器")
        self.resize(900, 700)  # 增加整体窗口大小，以显示更多内容
        self.setModal(True)
        
        # 创建滚动区域，确保界面自适应
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setFrameShape(QFrame.NoFrame)
        
        # 创建主容器窗口部件
        main_widget = QWidget()
        scroll_area.setWidget(main_widget)
        
        # 创建主布局
        main_layout = QVBoxLayout(main_widget)
        main_layout.setSpacing(10)  # 减小控件间距
        
        # 外层布局（用于包含滚动区域）
        outer_layout = QVBoxLayout(self)
        outer_layout.setContentsMargins(5, 5, 5, 5)  # 减小边距
        outer_layout.addWidget(scroll_area)
        
        # 创建表格显示选中的主机
        host_group = QGroupBox("已选主机:")
        host_layout = QVBoxLayout(host_group)
        host_layout.setContentsMargins(5, 10, 5, 5)  # 减小边距
        
        # 创建主机表格 - 调整样式与监控项对话框保持一致
        # 增加列数为5列，添加"可见名称"和"端口号"列
        # 大幅增加表格高度以显示更多主机数据行
        self.host_table = QTableWidget(0, 5)
        self.host_table.setHorizontalHeaderLabels(["主机ID", "IP地址", "主机名称", "可见名称", "端口号"])
        self.host_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
        self.host_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.host_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
        self.host_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
        self.host_table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Stretch)
        self.host_table.setMinimumHeight(200)  
        
        # 显示选中的主机
        if self.hosts:
            self.host_table.setRowCount(len(self.hosts))
            for i, host in enumerate(self.hosts):
                # 主机ID
                host_id = host.get('hostid', '')
                self.host_table.setItem(i, 0, QTableWidgetItem(host_id))
                
                # IP地址 - 可能有多个IP，用逗号分隔
                ips = self.host_ips.get(host_id, [])
                ip_text = ", ".join(ips) if ips else "-"
                self.host_table.setItem(i, 1, QTableWidgetItem(ip_text))
                
                # 主机名称
                host_name = ""
                if host_id in self.host_info_map:
                    host_name = self.host_info_map[host_id].get("host", "")
                self.host_table.setItem(i, 2, QTableWidgetItem(host_name))
                
                # 可见名称
                visible_name = host.get("name", "")
                self.host_table.setItem(i, 3, QTableWidgetItem(visible_name))
                
                # 端口号 - 直接尝试获取接口端口
                port_text = "161"  # 默认值为SNMP标准端口
                try:
                    # 尝试获取主机所有接口
                    interfaces = self.api._call("hostinterface.get", {
                        "hostids": [host_id],
                        "output": ["port", "type"]
                    })
                    
                    if interfaces:
                        # 收集所有接口端口
                        port_list = []
                        for interface in interfaces:
                            if interface.get("port"):
                                port_list.append(interface.get("port"))
                        
                        if port_list:
                            # 去重并用逗号连接所有端口
                            unique_ports = list(set(port_list))
                            port_text = ", ".join(unique_ports)
                        
                except Exception as e:
                    self.log_message(f"获取主机接口端口信息出错: {str(e)}")
                
                port_item = QTableWidgetItem(port_text)
                
                # 为不同类型的端口设置不同的背景色
                if "10050" in port_text:
                    port_item.setBackground(QColor(230, 255, 230))  # 淡绿色 - Zabbix Agent
                elif "161" in port_text:
                    port_item.setBackground(QColor(230, 230, 255))  # 淡蓝色 - SNMP
                
                self.host_table.setItem(i, 4, port_item)
        
        host_layout.addWidget(self.host_table)
        main_layout.addWidget(host_group)
        
        # 创建触发器表单
        form_group = QGroupBox("触发器信息:")
        form_layout = QFormLayout(form_group)
        form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
        
        # 触发器名称
        self.name_edit = QLineEdit()
        self.name_edit.setPlaceholderText("例如: CPU使用率过高")
        form_layout.addRow("* 名称:", self.name_edit)
        
        # 事件名称
        self.description_edit = QLineEdit()
        self.description_edit.setPlaceholderText("留空则使用名称")
        form_layout.addRow("事件名称:", self.description_edit)
        
        # 运算数据
        self.operand_edit = QLineEdit()
        self.operand_edit.setPlaceholderText("使用 {ITEM.LASTVALUE1}, {ITEM.LASTVALUE2} 等替换监控值")
        form_layout.addRow("运营数据:", self.operand_edit)
        
        # 严重性
        self.severity_combo = QComboBox()
        for level in ["未分类", "信息", "警告", "一般", "严重", "灾难"]:
            self.severity_combo.addItem(level)
        self.severity_combo.setCurrentIndex(3)  # 默认"一般严重"
        form_layout.addRow("严重性:", self.severity_combo)
        
        # 添加手动关闭选项
        self.manual_close_checkbox = QCheckBox("允许在问题解决前手动确认")
        self.manual_close_checkbox.setChecked(True)  # 默认勾选
        self.manual_close_checkbox.setToolTip("允许在问题解决前手动确认(关闭)触发器")
        form_layout.addRow("手动关闭:", self.manual_close_checkbox)
        
        # 禁用状态选项
        self.status_checkbox = QCheckBox("创建后禁用")
        
        main_layout.addWidget(form_group)
        
        # 创建表达式和复合表达式区域
        expr_group = QGroupBox("表达式设置:")
        expr_layout = QVBoxLayout(expr_group)
        
        # 添加表达式类型选择
        expr_type_layout = QHBoxLayout()
        self.expr_type_label = QLabel("表达式类型:")
        expr_type_layout.addWidget(self.expr_type_label)
        
        self.single_expr_radio = QRadioButton("单一条件")
        self.single_expr_radio.setChecked(True)
        self.single_expr_radio.toggled.connect(self.on_expr_type_changed)
        expr_type_layout.addWidget(self.single_expr_radio)
        
        self.compound_expr_radio = QRadioButton("复合条件")
        self.compound_expr_radio.toggled.connect(self.on_expr_type_changed)
        expr_type_layout.addWidget(self.compound_expr_radio)
        
        expr_type_layout.addStretch()
        expr_layout.addLayout(expr_type_layout)
        
        # 单一表达式区域
        self.single_expr_widget = QWidget()
        single_expr_layout = QFormLayout(self.single_expr_widget)
        single_expr_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
        
        # 添加说明标签
        self.condition_tip_label = QLabel("请在下方填写监控项键值和阈值")
        self.condition_tip_label.setStyleSheet("color: blue; font-weight: bold;")
        single_expr_layout.addRow("", self.condition_tip_label)
        
        # 监控项键值
        self.item_key_edit = QLineEdit()
        self.item_key_edit.setPlaceholderText("例如: system.cpu.util[,user]")
        single_expr_layout.addRow("* 监控项键值:", self.item_key_edit)
        
        # 函数选择
        self.function_combo = QComboBox()
        functions = [
            "last", "avg", "max", "min", "sum", "count", "change",
            "nodata", "diff", "prev", "abschange", "delta"
        ]
        self.function_combo.addItems(functions)
        self.function_combo.currentIndexChanged.connect(self.on_function_changed)
        single_expr_layout.addRow("函数:", self.function_combo)
        
        # 函数参数
        param_widget = QWidget()
        param_layout = QHBoxLayout(param_widget)
        param_layout.setContentsMargins(0, 0, 0, 0)
        
        # 参数类型选择 - 时间范围 / 数据点数量
        param_type_group = QWidget()
        param_type_layout = QHBoxLayout(param_type_group)
        param_type_layout.setContentsMargins(0, 0, 0, 0)
        
        self.time_param_radio = QRadioButton("时间范围")
        self.time_param_radio.setChecked(True)
        self.time_param_radio.toggled.connect(self.on_param_type_changed)
        
        self.count_param_radio = QRadioButton("数据点数量")
        self.count_param_radio.toggled.connect(self.on_param_type_changed)
        
        param_type_layout.addWidget(self.time_param_radio)
        param_type_layout.addWidget(self.count_param_radio)
        param_layout.addWidget(param_type_group)
        
        # 参数输入
        self.time_param_edit = QLineEdit()
        self.time_param_edit.setPlaceholderText("例如: 5m, 1h")
        self.time_param_edit.setText("5m")
        
        self.count_param_edit = QLineEdit()
        self.count_param_edit.setPlaceholderText("例如: 5")
        self.count_param_edit.setText("5")
        self.count_param_edit.setVisible(False)
        
        param_layout.addWidget(self.time_param_edit)
        param_layout.addWidget(self.count_param_edit)
        
        single_expr_layout.addRow("参数:", param_widget)
        
        # 操作符和阈值
        operator_threshold_widget = QWidget()
        operator_threshold_layout = QHBoxLayout(operator_threshold_widget)
        operator_threshold_layout.setContentsMargins(0, 0, 0, 0)
        
        self.operator_combo = QComboBox()
        operators = ["=", "<>", ">", ">=", "<", "<="]
        self.operator_combo.addItems(operators)
        self.operator_combo.setCurrentIndex(3)  # 默认 ">="
        
        self.threshold_edit = QLineEdit()
        self.threshold_edit.setPlaceholderText("例如: 90")
        
        operator_threshold_layout.addWidget(self.operator_combo)
        operator_threshold_layout.addWidget(self.threshold_edit)
        
        single_expr_layout.addRow("操作符和阈值:", operator_threshold_widget)
        
        # 创建按钮区域
        buttons_widget = QWidget()
        buttons_layout = QHBoxLayout(buttons_widget)
        buttons_layout.setContentsMargins(0, 0, 0, 0)
        
        # 预览按钮
        self.preview_btn = QPushButton("生成单条件预览")
        self.preview_btn.clicked.connect(self.on_preview_expr)
        buttons_layout.addWidget(self.preview_btn)
        
        # 添加条件按钮
        self.add_condition_btn = QPushButton("添加到条件列表")
        self.add_condition_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
        self.add_condition_btn.clicked.connect(self.on_add_expression)
        buttons_layout.addWidget(self.add_condition_btn)
        
        buttons_layout.addStretch()
        single_expr_layout.addRow("", buttons_widget)
        
        # 表达式预览
        self.expr_preview = QTextEdit()
        self.expr_preview.setReadOnly(True)
        self.expr_preview.setMaximumHeight(60)  # 增加高度以显示更多内容
        self.expr_preview.setPlaceholderText("点击\"生成单条件预览\"按钮预览单个条件的表达式")
        single_expr_layout.addRow("单条件预览:", self.expr_preview)
        
        expr_layout.addWidget(self.single_expr_widget)
        
        # 复合表达式区域
        self.compound_expr_widget = QWidget()
        self.compound_expr_widget.setVisible(False)
        compound_expr_layout = QVBoxLayout(self.compound_expr_widget)
        compound_expr_layout.setSpacing(5)  # 减小间距
        
        # 表达式列表
        self.expr_list_group = QGroupBox("条件列表")
        expr_list_layout = QVBoxLayout(self.expr_list_group)
        expr_list_layout.setContentsMargins(5, 10, 5, 5)  # 减小边距
        
        # 条件列表表格
        self.expr_table = QTableWidget(0, 4)
        self.expr_table.setHorizontalHeaderLabels(["监控项键值", "函数", "操作符", "阈值"])
        # 设置固定列宽而不是自动伸缩
        self.expr_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)  # 监控项键值列可伸缩
        self.expr_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Fixed)    # 函数列固定宽度
        self.expr_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Fixed)    # 操作符列固定宽度
        self.expr_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.Fixed)    # 阈值列固定宽度
        # 预设各列宽度
        self.expr_table.setColumnWidth(1, 100)  # 函数列宽度
        self.expr_table.setColumnWidth(2, 60)   # 操作符列宽度
        self.expr_table.setColumnWidth(3, 60)   # 阈值列宽度
        
        self.expr_table.setMinimumHeight(150)  # 增加最小高度
        self.expr_table.setMaximumHeight(200)  # 增加最大高度限制
        # 设置表格样式以突出显示
        self.expr_table.setStyleSheet("""
            QTableWidget {
                border: 1px solid #ccc;
                border-radius: 4px;
                background-color: #f9f9f9;
            }
            QTableWidget::item:selected {
                background-color: #e3f2fd;
            }
            QHeaderView::section {
                background-color: #e0e0e0;
                padding: 5px;
                font-weight: bold;
                border: 0px;
                border-bottom: 1px solid #ccc;
            }
        """)
        # 调整行高
        self.expr_table.verticalHeader().setDefaultSectionSize(30)
        expr_list_layout.addWidget(self.expr_table)
        
        # 按钮区域
        expr_buttons_layout = QHBoxLayout()
        
        self.edit_expr_btn = QPushButton("编辑条件")
        self.edit_expr_btn.clicked.connect(self.on_edit_expression)
        expr_buttons_layout.addWidget(self.edit_expr_btn)
        
        self.remove_expr_btn = QPushButton("删除条件")
        self.remove_expr_btn.clicked.connect(self.on_remove_expression)
        expr_buttons_layout.addWidget(self.remove_expr_btn)
        
        expr_buttons_layout.addStretch()
        expr_list_layout.addLayout(expr_buttons_layout)
        
        compound_expr_layout.addWidget(self.expr_list_group)
        
        # 操作符选择
        operators_group = QGroupBox("操作符")
        operators_layout = QVBoxLayout(operators_group)
        operators_layout.setContentsMargins(5, 10, 5, 5)  # 减小边距
        
        self.operator_btns_layout = QHBoxLayout()
        
        self.and_operator_radio = QRadioButton("AND (与)")
        self.and_operator_radio.setChecked(True)
        self.and_operator_radio.toggled.connect(self.update_compound_preview)
        self.operator_btns_layout.addWidget(self.and_operator_radio)
        
        self.or_operator_radio = QRadioButton("OR (或)")
        self.or_operator_radio.toggled.connect(self.update_compound_preview)
        self.operator_btns_layout.addWidget(self.or_operator_radio)
        
        operators_layout.addLayout(self.operator_btns_layout)
        compound_expr_layout.addWidget(operators_group)
        
        # 复合表达式预览
        preview_group = QGroupBox("复合表达式预览")
        preview_layout = QVBoxLayout(preview_group)
        preview_layout.setContentsMargins(5, 10, 5, 5)
        
        self.compound_expr_preview = QTextEdit()
        self.compound_expr_preview.setReadOnly(True)
        self.compound_expr_preview.setMinimumHeight(80)  # 增加最小高度
        self.compound_expr_preview.setMaximumHeight(120)  # 增加最大高度
        self.compound_expr_preview.setPlaceholderText("点击\"更新预览\"按钮查看条件组合的表达式")
        # 设置预览区样式
        self.compound_expr_preview.setStyleSheet("""
            QTextEdit {
                background-color: #f5f5f5;
                border: 1px solid #ddd;
                border-radius: 4px;
                padding: 5px;
                font-family: Consolas, Courier, monospace;
                font-size: 12px;
            }
        """)
        preview_layout.addWidget(self.compound_expr_preview)
        
        # 增加帮助提示标签
        help_label = QLabel("提示: 表达式将应用于所有选中的主机，可使用AND或OR连接多个条件")
        help_label.setStyleSheet("color: #666; font-size: 11px; font-style: italic;")
        preview_layout.addWidget(help_label)
        
        # 更新预览按钮 - 使其更明显
        update_preview_btn = QPushButton("更新预览")
        update_preview_btn.setStyleSheet("background-color: #4db6ac; color: white; font-weight: bold;")
        update_preview_btn.clicked.connect(self.update_compound_preview)
        # 使用HBoxLayout替代Qt.AlignRight
        btn_layout = QHBoxLayout()
        btn_layout.addStretch()
        btn_layout.addWidget(update_preview_btn)
        preview_layout.addLayout(btn_layout)
        
        compound_expr_layout.addWidget(preview_group)
        
        expr_layout.addWidget(self.compound_expr_widget)
        
        main_layout.addWidget(expr_group)
        
        # 函数类型信息 - 用于判断函数参数类型的合法性
        self.function_types = {
            "data_only": ["last", "prev", "diff"],  # 只支持数据点数量的函数
            "time_only": ["nodata"],  # 只支持时间范围的函数
            "optional_param": ["change", "abschange"]  # 可选参数的函数
        }
        
        # 恢复表达式设置
        recovery_group = QGroupBox("恢复表达式:")
        recovery_layout = QVBoxLayout(recovery_group)
        
        # 恢复模式选择
        recovery_mode_layout = QHBoxLayout()
        recovery_mode_layout.addWidget(QLabel("恢复模式:"))
        self.recovery_mode_combo = QComboBox()
        recovery_modes = ["使用问题表达式", "使用恢复表达式", "使用恢复标签"]
        self.recovery_mode_combo.addItems(recovery_modes)
        self.recovery_mode_combo.currentIndexChanged.connect(self.on_recovery_mode_changed)
        recovery_mode_layout.addWidget(self.recovery_mode_combo)
        recovery_layout.addLayout(recovery_mode_layout)
        
        # 创建恢复表达式表单
        self.recovery_form = QWidget()
        recovery_form_layout = QFormLayout(self.recovery_form)
        recovery_form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
        
        # 监控项键值 - 可以延用问题表达式的键值
        self.recovery_key_edit = QLineEdit()
        self.recovery_key_edit.setPlaceholderText("留空则使用问题表达式的键值")
        recovery_form_layout.addRow("监控项键值:", self.recovery_key_edit)
        
        # 函数选择
        self.recovery_function_combo = QComboBox()
        self.recovery_function_combo.addItems(functions)
        self.recovery_function_combo.currentIndexChanged.connect(self.on_recovery_function_changed)
        recovery_form_layout.addRow("函数:", self.recovery_function_combo)
        
        # 参数继承复选框
        self.param_inherit_layout = QHBoxLayout()
        self.recovery_param_inherit_check = QCheckBox("使用与问题表达式相同的参数")
        self.recovery_param_inherit_check.setChecked(True)
        self.recovery_param_inherit_check.stateChanged.connect(self.on_recovery_param_inherit_changed)
        self.param_inherit_layout.addWidget(self.recovery_param_inherit_check)
        recovery_form_layout.addRow("", self.param_inherit_layout)
        
        # 函数参数
        recovery_param_widget = QWidget()
        recovery_param_layout = QHBoxLayout(recovery_param_widget)
        recovery_param_layout.setContentsMargins(0, 0, 0, 0)
        
        # 参数类型选择 - 时间范围 / 数据点数量
        recovery_param_type_group = QWidget()
        recovery_param_type_layout = QHBoxLayout(recovery_param_type_group)
        recovery_param_type_layout.setContentsMargins(0, 0, 0, 0)
        
        self.recovery_time_param_radio = QRadioButton("时间范围")
        self.recovery_time_param_radio.setChecked(True)
        self.recovery_time_param_radio.toggled.connect(self.on_recovery_param_type_changed)
    
        self.recovery_count_param_radio = QRadioButton("数据点数量")
        self.recovery_count_param_radio.toggled.connect(self.on_recovery_param_type_changed)
        
        recovery_param_type_layout.addWidget(self.recovery_time_param_radio)
        recovery_param_type_layout.addWidget(self.recovery_count_param_radio)
        recovery_param_layout.addWidget(recovery_param_type_group)
        
        # 参数输入
        self.recovery_time_param_edit = QLineEdit()
        self.recovery_time_param_edit.setPlaceholderText("例如: 5m, 1h")
        self.recovery_time_param_edit.setText("5m")
        
        self.recovery_count_param_edit = QLineEdit()
        self.recovery_count_param_edit.setPlaceholderText("例如: 5")
        self.recovery_count_param_edit.setText("5")
        self.recovery_count_param_edit.setVisible(False)
        
        recovery_param_layout.addWidget(self.recovery_time_param_edit)
        recovery_param_layout.addWidget(self.recovery_count_param_edit)
        
        recovery_form_layout.addRow("参数:", recovery_param_widget)
        
        # 操作符和阈值
        recovery_operator_threshold_widget = QWidget()
        recovery_operator_threshold_layout = QHBoxLayout(recovery_operator_threshold_widget)
        recovery_operator_threshold_layout.setContentsMargins(0, 0, 0, 0)
        
        self.recovery_operator_combo = QComboBox()
        self.recovery_operator_combo.addItems(operators)
        self.recovery_operator_combo.setCurrentIndex(2)  # 默认 "<"
        
        self.recovery_threshold_edit = QLineEdit()
        self.recovery_threshold_edit.setPlaceholderText("例如: 80")
        
        recovery_operator_threshold_layout.addWidget(self.recovery_operator_combo)
        recovery_operator_threshold_layout.addWidget(self.recovery_threshold_edit)
        
        recovery_form_layout.addRow("操作符和阈值:", recovery_operator_threshold_widget)
        
        # 由于使用问题表达式作为恢复条件时不需要这些设置，默认隐藏
        self.recovery_form.setVisible(False)
        
        recovery_layout.addWidget(self.recovery_form)
        main_layout.addWidget(recovery_group)
        
        # Excel导入导出区域
        if PANDAS_AVAILABLE:
            excel_group = QGroupBox("Excel导入导出:")
            excel_layout = QHBoxLayout(excel_group)
            
            self.excel_template_btn = QPushButton("下载模板")
            self.excel_template_btn.clicked.connect(self.download_excel_template)
            excel_layout.addWidget(self.excel_template_btn)
            
            self.excel_import_btn = QPushButton("导入触发器")
            self.excel_import_btn.clicked.connect(self.import_from_excel)
            excel_layout.addWidget(self.excel_import_btn)
            
            excel_layout.addStretch()
            
            main_layout.addWidget(excel_group)
        
        # 底部按钮
        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        main_layout.addWidget(button_box)
        
        # 初始化UI状态
        self.on_recovery_mode_changed(0)
        self.on_recovery_param_inherit_changed(True)
        
        # 记录日志，帮助调试API交互
        self.log_message("触发器对话框初始化完成")
        self.log_message(f"已加载 {len(self.hosts)} 台主机")

    def on_expr_type_changed(self, checked):
        """处理表达式类型变化"""
        is_compound = self.compound_expr_radio.isChecked()
        
        # 修改：单一条件表单和复合条件表格始终可见
        self.single_expr_widget.setVisible(True)  # 始终保持表单可见
        self.compound_expr_widget.setVisible(is_compound)
        
        # 更新提示标签
        if is_compound:
            # 更新标签，指示此表单用于添加条件
            self.condition_tip_label.setText("请填写表单添加条件到下方条件列表中")
            self.condition_tip_label.setStyleSheet("color: #4CAF50; font-weight: bold;")
            # 更新按钮文本，明确作用
            self.add_condition_btn.setText("添加到条件列表")
            self.add_condition_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
        else:
            # 恢复原始标签
            self.condition_tip_label.setText("请在下方填写监控项键值和阈值")
            self.condition_tip_label.setStyleSheet("color: blue; font-weight: bold;")
            # 恢复按钮文本
            self.add_condition_btn.setText("添加单条件")
            self.add_condition_btn.setStyleSheet("")
        
        # 更新日志
        expr_type = "复合条件" if is_compound else "单一条件"
        self.log_message(f"切换表达式类型为: {expr_type}")

    def on_add_expression(self):
        """添加表达式条件"""
        # 获取表单数据
        item_key = self.item_key_edit.text().strip()
        function_name = self.function_combo.currentText()
        operator = self.operator_combo.currentText()
        threshold = self.threshold_edit.text().strip()
        
        # 验证
        if not item_key or not threshold:
            QMessageBox.warning(self, "验证失败", "监控项键值和阈值不能为空")
            return
        
        # 获取参数
        param_value = ""
        is_count_param = self.count_param_radio.isChecked()
        
        if is_count_param:
            count_value = self.count_param_edit.text().strip()
            if count_value:
                param_value = f"#{count_value}"
        else:
            time_value = self.time_param_edit.text().strip()
            if time_value:
                param_value = time_value
                # 确保时间参数有单位
                if not any(unit in param_value for unit in ["s", "m", "h", "d"]):
                    param_value = f"{param_value}s"  # 默认添加秒单位
        
        # 创建表达式
        expression = {
            "item_key": item_key,
            "function": function_name,
            "operator": operator,
            "threshold": threshold,
            "param": param_value,
            "is_count_param": is_count_param
        }
        
        # 预览表达式
        if self.hosts and len(self.hosts) > 0:
            host = self.hosts[0]
            host_technical_name = self.get_host_technical_name(host)
            if host_technical_name:
                expr = self.build_single_expression(expression, host_technical_name)
                expression["preview"] = expr
                self.log_message(f"生成单个表达式: {expr}")
            else:
                self.log_message("警告：无法获取主机技术名称，无法生成预览")
        else:
            self.log_message("警告：没有选择主机，无法生成预览")
        
        # 添加到列表 - 确保列表已初始化
        if not hasattr(self, 'expressions'):
            self.expressions = []
        
        self.expressions.append(expression)
        self.log_message(f"添加新条件到列表，当前条件数量: {len(self.expressions)}")
        
        # 更新表格
        self.update_expression_table()
        
        # 清空表单
        self.item_key_edit.clear()
        self.threshold_edit.clear()
        
        # 更新日志
        self.log_message(f"添加新条件: {item_key} - {function_name} - {operator} - {threshold}")
        
        # 自动切换到复合表达式视图
        self.compound_expr_radio.setChecked(True)
        
        # 更新复合表达式预览
        self.update_compound_preview()

    def on_edit_expression(self):
        """编辑表达式条件"""
        # 获取选中的行
        selected_rows = self.expr_table.selectedIndexes()
        if not selected_rows:
            QMessageBox.warning(self, "编辑失败", "请先选择要编辑的条件")
            return
        
        row = selected_rows[0].row()
        if row < 0 or row >= len(self.expressions):
            return
        
        # 获取表达式数据
        expression = self.expressions[row]
        
        # 填充表单
        self.item_key_edit.setText(expression["item_key"])
        
        # 设置函数
        index = self.function_combo.findText(expression["function"])
        if index >= 0:
            self.function_combo.setCurrentIndex(index)
        
        # 设置操作符
        index = self.operator_combo.findText(expression["operator"])
        if index >= 0:
            self.operator_combo.setCurrentIndex(index)
        
        # 设置阈值
        self.threshold_edit.setText(expression["threshold"])
        
        # 设置参数类型和值
        if expression["is_count_param"]:
            self.count_param_radio.setChecked(True)
            if expression["param"]:
                # 从 "#5" 格式提取数字
                param = expression["param"].replace("#", "")
                self.count_param_edit.setText(param)
        else:
            self.time_param_radio.setChecked(True)
            self.time_param_edit.setText(expression["param"])
        
        # 删除该表达式
        self.expressions.pop(row)
        self.update_expression_table()
        
        # 更新日志
        self.log_message(f"编辑条件: {expression['item_key']}")
        
        # 切换到单一表达式视图
        self.single_expr_radio.setChecked(True)

    def on_remove_expression(self):
        """删除表达式条件"""
        # 获取选中的行
        selected_rows = self.expr_table.selectedIndexes()
        if not selected_rows:
            QMessageBox.warning(self, "删除失败", "请先选择要删除的条件")
            return
        
        row = selected_rows[0].row()
        if row < 0 or row >= len(self.expressions):
            return
        
        # 获取表达式数据
        expression = self.expressions[row]
        
        # 删除该表达式
        self.expressions.pop(row)
        self.update_expression_table()
        
        # 更新日志
        self.log_message(f"删除条件: {expression['item_key']}")
        
        # 更新复合表达式预览
        self.update_compound_preview()
        
        # 如果没有表达式了，切换回单一表达式视图
        if not self.expressions:
            self.single_expr_radio.setChecked(True)

    def update_expression_table(self):
        """更新表达式表格"""
        # 确保表达式列表已初始化
        if not hasattr(self, 'expressions') or self.expressions is None:
            self.expressions = []
            self.log_message("警告：表达式列表未初始化，已创建空列表")
        
        # 记录日志
        self.log_message(f"更新表达式表格，条件数量: {len(self.expressions)}")
        
        # 设置表格行数
        self.expr_table.setRowCount(len(self.expressions))
        
        for i, expr in enumerate(self.expressions):
            # 序号列（垂直表头）
            self.expr_table.setVerticalHeaderItem(i, QTableWidgetItem(str(i+1)))
            
            # 监控项键值
            item_key_item = QTableWidgetItem(expr["item_key"])
            item_key_item.setToolTip(expr["item_key"])  # 添加tooltip便于查看完整内容
            self.expr_table.setItem(i, 0, item_key_item)
            
            # 函数
            function_text = f"{expr['function']}({expr['param']})" if expr["param"] else expr["function"]
            function_item = QTableWidgetItem(function_text)
            function_item.setToolTip(function_text)
            self.expr_table.setItem(i, 1, function_item)
            
            # 操作符
            operator_item = QTableWidgetItem(expr["operator"])
            # 根据不同操作符设置不同背景色
            if expr["operator"] in [">", ">="]:
                operator_item.setBackground(QColor(255, 235, 235))  # 淡红色
            elif expr["operator"] in ["<", "<="]:
                operator_item.setBackground(QColor(235, 255, 235))  # 淡绿色
            self.expr_table.setItem(i, 2, operator_item)
            
            # 阈值
            threshold_item = QTableWidgetItem(expr["threshold"])
            # 不使用Qt.AlignCenter
            self.expr_table.setItem(i, 3, threshold_item)
        
        # 不再调用resizeColumnsToContents()，使用固定列宽
        # 只调整行高
        self.expr_table.resizeRowsToContents()
        
        # 如果有足够多的表达式，启用交替行背景色
        if len(self.expressions) > 1:
            self.expr_table.setAlternatingRowColors(True)
        else:
            self.expr_table.setAlternatingRowColors(False)
        
        # 如果没有表达式，自动切换到单一表达式模式
        if not self.expressions:
            self.single_expr_radio.setChecked(True)

    def update_compound_preview(self):
        """更新复合表达式预览"""
        # 确保复合表达式控件已创建
        if not hasattr(self, 'compound_expr_preview'):
            self.log_message("警告：复合表达式预览控件未创建")
            return
            
        # 确保表达式列表已初始化
        if not hasattr(self, 'expressions') or self.expressions is None:
            self.expressions = []
            self.log_message("警告：表达式列表未初始化，已创建空列表")
        
        if not self.expressions:
            self.compound_expr_preview.setText("尚未添加任何条件，请先添加条件")
            self.compound_expr_preview.setStyleSheet("color: #FF7043;")
            return
            
        if not self.hosts:
            self.compound_expr_preview.setText("没有选择主机，无法生成预览")
            self.compound_expr_preview.setStyleSheet("color: #FF7043;")
            return
        
        try:
            # 获取第一个主机用于预览
            host = self.hosts[0]
            host_technical_name = self.get_host_technical_name(host)
            
            if not host_technical_name:
                self.compound_expr_preview.setText("无法获取主机技术名称，无法生成预览")
                self.compound_expr_preview.setStyleSheet("color: #FF7043;")
                return
            
            # 获取操作符
            logical_operator = "and" if self.and_operator_radio.isChecked() else "or"
            
            # 生成表达式
            expressions = []
            for expr in self.expressions:
                single_expr = self.build_single_expression(expr, host_technical_name)
                if single_expr:
                    expressions.append(single_expr)
            
            if not expressions:
                self.compound_expr_preview.setText("无法生成有效表达式")
                self.compound_expr_preview.setStyleSheet("color: #FF7043;")
                return
            
            # 组合表达式
            compound_expr = f" {logical_operator} ".join(expressions)
            self.compound_expr_preview.setText(compound_expr)
            self.log_message(f"生成复合表达式预览: {compound_expr}")
        except Exception as e:
            self.log_message(f"更新复合表达式预览时出错: {str(e)}")
            self.compound_expr_preview.setText(f"生成预览出错: {str(e)}")
            return
    
    def build_single_expression(self, expression, host_technical_name):
        """构建单个表达式"""
        try:
            item_key = expression["item_key"]
            function_name = expression["function"]
            operator = expression["operator"]
            threshold = expression["threshold"]
            param_value = expression["param"]
            
            # 构建表达式
            if function_name in self.function_types["time_only"]:
                # nodata等只支持时间范围的函数
                nodata_time = param_value if param_value else "5m"
                # 确保时间参数有单位
                if not any(unit in nodata_time for unit in ["s", "m", "h", "d"]):
                    nodata_time = f"{nodata_time}m"  # 默认添加分钟单位
                return f"{function_name}(/{host_technical_name}/{item_key},{nodata_time})=1"
            elif function_name in self.function_types["data_only"]:
                # last等只支持数据点数量的函数
                if param_value:
                    return f"{function_name}(/{host_technical_name}/{item_key},{param_value}){operator}{threshold}"
                else:
                    return f"{function_name}(/{host_technical_name}/{item_key}){operator}{threshold}"
            elif function_name in self.function_types["optional_param"] and not param_value:
                # prev, diff等可选参数的函数，无参数形式
                return f"{function_name}(/{host_technical_name}/{item_key}){operator}{threshold}"
            else:
                # 聚合函数和其他函数
                if not param_value:
                    # 如果没有提供参数，使用默认值
                    if expression["is_count_param"]:
                        param_value = "#1"  # 默认1个数据点
                    else:
                        param_value = "5m"  # 默认5分钟
                
                # 对于所有时间参数，确保有时间单位
                if not param_value.startswith("#") and not any(unit in param_value for unit in ["s", "m", "h", "d"]):
                    param_value = f"{param_value}s"  # 默认添加秒单位
            
            return f"{function_name}(/{host_technical_name}/{item_key},{param_value}){operator}{threshold}"
        except Exception as e:
            self.log_message(f"构建表达式时出错: {str(e)}")
            return ""
    
    def import_from_excel(self):
        """从Excel导入触发器"""
        try:
            if not PANDAS_AVAILABLE:
                QMessageBox.warning(self, "缺少依赖", "请安装pandas和openpyxl库以支持Excel功能")
                return

            # 打开文件对话框
            file_path, _ = QFileDialog.getOpenFileName(
                self,
                "选择Excel文件",
                "",
                "Excel文件 (*.xlsx)"
            )

            if not file_path:
                return

            # 读取Excel文件
            df = pd.read_excel(file_path)

            # 单一表达式模式下的必需字段
            required_columns = ["触发器名称", "监控项键值", "函数", "操作符", "阈值"]

            # 基本验证
            for col in required_columns:
                if col not in df.columns:
                    QMessageBox.warning(self, "格式错误", f"Excel文件缺少必需列: {col}")
                    return

            # 严重性映射
            severity_map = {
                "信息": 1,
                "警告": 2,
                "一般": 3,
                "严重": 4,
                "灾难": 5
            }

            # 函数默认时间值
            function_time_defaults = {
                "avg": "5m",
                "min": "5m",
                "max": "5m",
                "sum": "5m",
                "count": "5m",
                "nodata": "5m"
            }

            # 创建的触发器数据列表
            triggers = []

            # 处理每一行
            for index, row in df.iterrows():
                try:
                    # 检查触发器名称必填
                    if pd.isna(row["触发器名称"]):
                        continue

                    # 获取基本触发器信息并确保所有数据都是字符串格式
                    trigger_name = str(row["触发器名称"]).strip()

                    # 检查必要字段
                    if pd.isna(row.get("监控项键值")) or pd.isna(row.get("函数")):
                        row_index = str(index)  # 将index转为字符串
                        self.log_message(f"警告: 行{row_index}缺少必要字段，将被跳过")
                        continue

                    # 跳过示例数据
                    if trigger_name.startswith("[示例]"):
                        self.log_message(f"跳过带[示例]标记的数据: {trigger_name}")
                        continue

                    # 过滤掉旧版模板的示例数据
                    if (trigger_name == "CPU使用率过高" and "system.cpu.util" in str(row.get("监控项键值", ""))) or \
                       (trigger_name == "内存使用率超限" and "vm.memory.util" in str(row.get("监控项键值", ""))) or \
                       (trigger_name == "磁盘空间不足" and "vfs.fs.size" in str(row.get("监控项键值", ""))):
                        self.log_message(f"跳过Excel模板中的示例数据: {trigger_name}")
                        continue

                    # 获取选中的主机
                    selected_hosts = self.get_selected_hosts()
                    if not selected_hosts:
                        QMessageBox.warning(self, "未选择主机", "请至少选择一个主机")
                        return

                    # 处理严重性
                    severity = 0  # 默认为未分类
                    if "严重性" in df.columns and not pd.isna(row.get("严重性")):
                        severity_text = str(row.get("严重性")).strip()
                        if severity_text in severity_map:
                            severity = severity_map[severity_text]
                        elif severity_text.isdigit():
                            severity = int(severity_text)

                    # 处理描述
                    description = ""
                    if "描述" in df.columns and not pd.isna(row.get("描述")):
                        description = str(row.get("描述")).strip()

                    # 处理启用状态
                    status = "0"  # 默认启用
                    if "是否启用" in df.columns and not pd.isna(row.get("是否启用")):
                        status_text = str(row.get("是否启用")).strip().lower()
                        if status_text in ["否", "no", "false", "0"]:
                            status = "1"  # 禁用

                    # 处理手动关闭选项
                    manual_close = "1"  # 默认允许手动关闭
                    if "允许手动关闭" in df.columns and not pd.isna(row.get("允许手动关闭")):
                        manual_close_text = str(row.get("允许手动关闭")).strip().lower()
                        if manual_close_text in ["否", "no", "false", "0"]:
                            manual_close = "0"  # 不允许手动关闭

                    for host in selected_hosts:
                        # 获取主机ID和名称
                        host_id = host.get("hostid")
                        visible_name = host.get("name", host.get("host", "未知主机"))

                        # 获取主机技术名称
                        host_technical_name = self.get_host_technical_name(host)
                        if not host_technical_name:
                            self.log_message(f"无法获取主机 {visible_name} 的技术名称，跳过该主机")
                            continue

                        # 处理单一表达式
                        item_key = str(row.get("监控项键值", "")).strip()

                        # 修复键值中可能存在的格式问题
                        if "[," in item_key:
                            # 修复像 "system.cpu.util[,user]" 这样的格式
                            item_key = item_key.replace("[,", "[*,")
                            self.log_message(f"Excel导入: 修复键值格式，添加缺失的参数: {item_key}")

                        function_name = str(row.get("函数", "")).lower().strip()

                        # 获取操作符
                        operator = str(row.get("操作符", "")).strip() if not pd.isna(row.get("操作符", "")) else ""

                        # 获取阈值
                        threshold = str(row.get("阈值", "")).strip() if not pd.isna(row.get("阈值", "")) else ""

                        # 获取函数参数类型和值
                        is_count_param = True  # 默认使用数据点数
                        param_value = ""

                        if "函数参数类型" in df.columns and not pd.isna(row.get("函数参数类型")):
                            param_type = str(row.get("函数参数类型")).strip()
                            if "时间" in param_type:
                                is_count_param = False

                        if is_count_param:
                            if "数据点数量" in df.columns and not pd.isna(row.get("数据点数量")):
                                # 获取数据点数量并确保格式正确
                                count_value = str(row.get("数据点数量")).strip()
                                # 移除可能存在的小数点，保留整数部分
                                if '.' in count_value:
                                    count_value = str(int(float(count_value)))
                                # 确保添加#前缀，但不重复添加
                                if not count_value.startswith('#'):
                                    param_value = f"#{count_value}"
                                else:
                                    param_value = count_value
                            else:
                                if "时间参数" in df.columns and not pd.isna(row.get("时间参数")):
                                    param_value = str(row.get("时间参数")).strip()
                                    # 确保时间参数有单位
                                    if not any(unit in param_value for unit in ["s", "m", "h", "d"]):
                                        param_value = f"{param_value}m"  # 默认添加分钟单位
                                elif function_name in function_time_defaults:
                                    # 使用函数的默认时间值
                                    param_value = function_time_defaults[function_name]

                        # 构建表达式
                        if function_name in self.function_types["time_only"]:
                            # nodata等只支持时间范围的函数
                            nodata_time = param_value if param_value else "5m"
                            # 确保时间参数有单位
                            if not any(unit in nodata_time for unit in ["s", "m", "h", "d"]):
                                nodata_time = f"{nodata_time}m"  # 默认添加分钟单位
                            expr = f"{function_name}(/{host_technical_name}/{item_key},{nodata_time})=1"
                            self.log_message(f"生成时间类函数表达式: {expr}")
                        elif function_name in self.function_types["data_only"]:
                            # last等只支持数据点数量的函数
                            if param_value:
                                # 确保数据点数量参数以#开头
                                if is_count_param and not param_value.startswith('#'):
                                    param_value = f"#{param_value}"
                                # Zabbix要求last函数的格式严格遵循: last(/host/key,#N)
                                self.log_message(f"构建数据点函数表达式，函数={function_name}，主机={host_technical_name}，键值={item_key}，参数={param_value}")
                                expr = f"{function_name}(/{host_technical_name}/{item_key},{param_value}){operator}{threshold}"
                                self.log_message(f"生成的表达式: {expr}")
                            else:
                                expr = f"{function_name}(/{host_technical_name}/{item_key}){operator}{threshold}"
                                self.log_message(f"生成的表达式(无参数): {expr}")
                        elif function_name in self.function_types["optional_param"] and not param_value:
                            # prev, diff等可选参数的函数，无参数形式
                            expr = f"{function_name}(/{host_technical_name}/{item_key}){operator}{threshold}"
                            self.log_message(f"生成可选参数函数表达式(无参数): {expr}")
                        else:
                            # 聚合函数和其他函数
                            # 确保时间参数有单位
                            if param_value:
                                if not param_value.startswith("#") and not any(unit in param_value for unit in ["s", "m", "h", "d"]):
                                    param_value = f"{param_value}s"  # 默认添加秒单位

                                expr = f"{function_name}(/{host_technical_name}/{item_key},{param_value}){operator}{threshold}"
                                self.log_message(f"生成聚合函数表达式: {expr}")
                            else:
                                # 如果没有参数但函数需要参数，使用默认值
                                default_param = "#1" if is_count_param else "5m"
                                expr = f"{function_name}(/{host_technical_name}/{item_key},{default_param}){operator}{threshold}"
                                self.log_message(f"生成聚合函数表达式(使用默认参数): {expr}")

                        # 创建触发器数据
                        trigger_data = {
                            "description": trigger_name,
                            "expression": expr,
                            "priority": str(severity),
                            "comments": description,
                            "status": status,
                            "manual_close": manual_close  # 允许手动关闭触发器
                        }

                        # 统一使用问题表达式作为恢复条件
                        trigger_data["recovery_mode"] = "0"

                        # 添加触发器数据到列表
                        triggers.append(trigger_data)
                        self.log_message(f"已准备好主机 {visible_name} 的触发器数据")

                except Exception as e:
                    # 将index转换为字符串避免类型错误
                    row_index = str(index) if index is not None else "未知"
                    error_msg = f"处理Excel行 {row_index} 失败: {str(e)}"
                    self.log_message(error_msg)
                    # 添加更详细的错误日志信息，包括异常类型
                    import traceback
                    logger.error(f"Excel处理错误详情: {error_msg}")
                    logger.error(traceback.format_exc())
                    continue

            # 批量创建所有触发器
            if triggers:
                try:
                    result = self.api.create_triggers(triggers)
                    # 确保result是字典类型并包含triggerids键
                    if isinstance(result, dict) and "triggerids" in result:
                        try:
                            created_trigger_ids = result.get("triggerids", [])
                            success_message = f"成功导入 {len(created_trigger_ids)} 个触发器"
                            QMessageBox.information(self, "导入成功", success_message)
                            self.log_message(success_message)
                        except Exception as result_err:
                            self.log_message(f"处理API返回结果出错: {str(result_err)}")
                            QMessageBox.information(self, "导入可能成功", "创建触发器请求已发送，但无法确认结果")
                    else:
                        self.log_message(f"触发器创建结果: {result}")
                        QMessageBox.warning(self, "导入部分成功", "导入完成，但部分触发器可能未成功创建")
                except Exception as e:
                    self.log_message(f"创建触发器失败: {str(e)}")
                    QMessageBox.critical(self, "导入失败", f"创建触发器时发生错误: {str(e)}")
                    return
            else:
                QMessageBox.warning(self, "导入失败", "没有有效的触发器数据可供导入")
                self.log_message("Excel导入：没有有效的触发器数据可供导入")

        except Exception as e:
            QMessageBox.critical(self, "导入失败", f"无法导入Excel文件: {str(e)}")
            self.log_message(f"导入Excel文件失败: {str(e)}")
            logger.error(f"导入Excel文件失败: {e}")

    def download_excel_template(self):
        """下载触发器导入的Excel模板"""
        try:
            # 这里增加日志记录
            self.log_message("开始下载Excel模板...")
            
            if not PANDAS_AVAILABLE:
                QMessageBox.warning(self, "缺少依赖", "请安装pandas和openpyxl库以支持Excel功能")
                return
            
            # 打开文件对话框，选择保存位置
            file_path, _ = QFileDialog.getSaveFileName(
                self, "保存Excel模板", "触发器导入模板.xlsx", "Excel文件 (*.xlsx)"
            )
            
            if not file_path:
                return
            
            if not file_path.endswith('.xlsx'):
                file_path += '.xlsx'
            
            # 创建示例数据 - 仅包含单一表达式示例
            sample_data = {
                "触发器名称": [
                    "[示例] CPU使用率过高", 
                    "[示例] 内存使用率超限", 
                    "[示例] 磁盘空间不足",
                    "[示例] 监控项状态异常",
                    "[示例] 系统负载过高"
                ],
                "监控项键值": [
                    "system.cpu.util[all,user]", 
                    "vm.memory.util", 
                    "vfs.fs.size[/,pfree]",
                    "system.run[ps -ef | grep nginx | wc -l]",
                    "system.cpu.load[percpu,avg1]"
                ],
                "函数": [
                    "last", 
                    "avg", 
                    "last",
                    "last",
                    "min"
                ],
                "函数参数类型": [
                    "数据点数量", 
                    "时间范围", 
                    "数据点数量",
                    "数据点数量",
                    "时间范围"
                ],
                "数据点数量": [
                    "3", 
                    "", 
                    "5",
                    "1",
                    ""
                ],
                "时间参数": [
                    "", 
                    "5m", 
                    "",
                    "",
                    "10m"
                ],
                "操作符": [
                    ">", 
                    ">", 
                    "<",
                    ">",
                    ">"
                ],
                "阈值": [
                    "90", 
                    "80", 
                    "10",
                    "5",
                    "3"
                ],
                "严重性": [
                    "一般", 
                    "严重", 
                    "灾难",
                    "严重",
                    "一般"
                ],
                "描述": [
                    "CPU使用率过高超过90%", 
                    "过去5分钟平均内存使用率超过80%", 
                    "磁盘剩余空间小于10%",
                    "Nginx进程数量超过5个",
                    "系统负载超过3"
                ],
                "是否启用": [
                    "是", 
                    "是", 
                    "是",
                    "是",
                    "是"
                ],
                "允许手动关闭": [
                    "是",
                    "是",
                    "是",
                    "是",
                    "是"
                ],
                "表达式说明": [
                    "使用last函数检测CPU使用率",
                    "使用avg函数计算过去时间段的平均值",
                    "检测磁盘剩余空间百分比",
                    "检测特定进程的数量",
                    "使用min函数检测最小负载值"
                ],
                "注意事项": [
                    "数据点数量参数会自动添加#前缀",
                    "时间参数必须包含单位(s/m/h/d)",
                    "注意阈值和操作符的配合使用",
                    "复合表达式请在界面中创建，不支持Excel导入",
                    "函数参数根据函数类型有不同的要求"
                ]
            }
            
            # 创建DataFrame
            df = pd.DataFrame(sample_data)
            
            # 创建使用说明sheet
            try:
                # 先检查文件是否可写入
                try:
                    # 尝试以写入模式打开文件，检查是否有权限
                    with open(file_path, 'wb') as test_file:
                        pass
                except PermissionError:
                    error_msg = f"无法写入文件: {file_path}\n\n可能原因：\n1. 文件已被其他程序（如Excel）打开\n2. 您没有该位置的写入权限\n3. 文件被标记为只读\n\n建议：\n- 关闭所有Excel程序\n- 尝试保存到其他文件夹\n- 以管理员身份运行程序"
                    self.log_message(f"权限错误: {error_msg}")
                    QMessageBox.critical(self, "权限错误", error_msg)
                    return
                except Exception as io_err:
                    self.log_message(f"文件IO测试失败: {io_err}")
                    # 继续尝试保存，可能会在实际写入时捕获更具体的错误
                
                with pd.ExcelWriter(file_path, engine='openpyxl') as writer:
                    df.to_excel(writer, sheet_name='触发器模板', index=False)
                    
                    # 添加使用说明sheet
                    instructions = pd.DataFrame({
                        "使用说明": [
                            "1. 此模板包含单一表达式和复合表达式两种类型的触发器示例",
                            "2. 对于单一表达式，需填写基本字段(触发器名称、监控项键值、函数等)",
                            "3. 对于复合表达式，仅需填写'触发器名称'和'复合表达式'两个字段",
                            "4. 复合表达式模式下，'是否复合表达式'设为'是'，其他单一表达式字段可留空",
                            "5. 复合表达式中可以使用/HOST/占位符，系统会自动替换为正确的主机名",
                            "6. 复合表达式可以使用and、or和not逻辑运算符组合多个条件",
                            "7. 常用函数: last(最新值), avg(平均值), min(最小值), max(最大值), nodata(无数据)",
                            "8. 函数参数可以是数据点数量(1, 2, 3...)或时间范围(10s, 5m, 1h, 1d)",
                            "9. 导入前请删除所有示例行和注释行(标记为[示例]的行)"
                        ]
                    })
                    instructions.to_excel(writer, sheet_name='使用说明', index=False)
                    
                    # 添加复合表达式示例sheet
                    examples = pd.DataFrame({
                        "复合表达式模式": [
                            "两个条件AND关系",
                            "两个条件OR关系",
                            "多条件混合逻辑",
                            "时间阈值条件",
                            "不同监控项组合"
                        ],
                        "表达式示例": [
                            "last(/HOST/system.cpu.util)>80 and last(/HOST/vm.memory.util)>90",
                            "last(/HOST/vfs.fs.size[/,pfree])<10 or last(/HOST/vfs.fs.size[/data,pfree])<5",
                            "(last(/HOST/system.cpu.util)>90 or last(/HOST/system.cpu.load[percpu,avg1])>5) and last(/HOST/vm.memory.util)>80",
                            "last(/HOST/net.if.in[eth0])<>0 and nodata(/HOST/agent.ping,5m)=1",
                            "last(/HOST/system.cpu.util)>80 and last(/HOST/proc.num[])>500"
                        ],
                        "说明": [
                            "CPU使用率大于80%且内存使用率大于90%（注：程序会自动替换/HOST/）",
                            "根分区剩余空间小于10%或data分区剩余空间小于5%（注：程序会自动替换/HOST/）",
                            "(CPU使用率大于90%或系统负载大于5) 且 内存使用率大于80%（注：程序会自动替换/HOST/）",
                            "网卡有流量但Agent连续5分钟无数据（注：程序会自动替换/HOST/）",
                            "CPU使用率大于80%且进程数超过500（注：程序会自动替换/HOST/）"
                        ]
                    })
                    examples.to_excel(writer, sheet_name='复合表达式示例', index=False)
                    
                    # 添加特别说明sheet强调自动替换功能
                    special_notes = pd.DataFrame({
                        "关于/HOST/自动替换功能": [
                            "【重要提示】",
                            "1. 在复合表达式中，您只需使用/HOST/作为主机名的占位符",
                            "2. 系统会在导入时自动将/HOST/替换为当前选择的主机技术名称",
                            "3. 您不需要手动查找或输入每个主机的技术名称",
                            "4. 对于批量创建，系统会为每个选中的主机分别替换/HOST/",
                            "5. 这大大简化了复合表达式的创建过程，尤其是批量创建时",
                            "",
                            "使用复合表达式的优势：",
                            "1. 可以创建更复杂的条件判断逻辑",
                            "2. 可以在一个触发器中引用多个不同的监控项",
                            "3. 使用复合表达式时，只需填写'触发器名称'和'复合表达式'字段",
                            "4. 其他字段（如'监控项键值'、'函数'等）在复合表达式模式下无需填写",
                            "",
                            "示例：",
                            "last(/HOST/system.cpu.util)>90",
                            "↓ 系统自动替换为 ↓",
                            "last(/Server1/system.cpu.util)>90  (用于主机Server1)",
                            "last(/DB-Server/system.cpu.util)>90  (用于主机DB-Server)"
                        ]
                    })
                    special_notes.to_excel(writer, sheet_name='HOST自动替换说明', index=False)
                    
                    # 添加复合表达式使用说明sheet
                    compound_instructions = pd.DataFrame({
                        "复合表达式使用方法": [
                            "【字段填写说明】",
                            "使用单一表达式时：",
                            "- 触发器名称: 必填",
                            "- 监控项键值: 必填",
                            "- 函数: 必填",
                            "- 操作符: 必填",
                            "- 阈值: 必填",
                            "- 是否复合表达式: 设为'否'",
                            "- 复合表达式: 留空",
                            "",
                            "使用复合表达式时：",
                            "- 触发器名称: 必填",
                            "- 监控项键值: 可留空（不使用）",
                            "- 函数: 可留空（不使用）",
                            "- 操作符: 可留空（不使用）",
                            "- 阈值: 可留空（不使用）",
                            "- 是否复合表达式: 设为'是'",
                            "- 复合表达式: 必填，包含完整的触发器条件"
                        ]
                    })
                    compound_instructions.to_excel(writer, sheet_name='复合表达式字段说明', index=False)
            except PermissionError as perm_err:
                error_msg = f"保存文件失败: 没有写入权限\n\n可能原因：\n1. 文件已被其他程序（如Excel）打开\n2. 您没有该位置的写入权限\n3. 文件被标记为只读\n\n建议：\n- 关闭所有Excel程序\n- 尝试保存到其他文件夹（如\"文档\"）\n- 以管理员身份运行程序"
                self.log_message(f"权限错误: {str(perm_err)}")
                QMessageBox.critical(self, "保存失败", error_msg)
                return
            except Exception as sheet_err:
                # 如果无法创建多sheet，则尝试只保存主sheet
                self.log_message(f"无法创建多sheet Excel: {str(sheet_err)}，将尝试只保存主数据表")
                
                try:
                    # 再次尝试保存，但只保存主数据表
                    df.to_excel(file_path, index=False)
                    self.log_message(f"成功保存了简化版Excel模板")
                    QMessageBox.information(self, "部分保存成功", 
                                        f"由于以下原因，只能保存基础模板而不包含帮助和示例:\n{str(sheet_err)}\n\n"
                                        f"基础模板已保存到: {file_path}")
                    return
                except PermissionError:
                    error_msg = f"保存简化版模板也失败，没有写入权限。\n\n建议：\n- 关闭所有Excel程序\n- 尝试保存到其他文件夹\n- 以管理员身份运行程序"
                    self.log_message(f"二次保存时遇到权限错误")
                    QMessageBox.critical(self, "保存失败", error_msg)
                    return
                except Exception as final_err:
                    error_msg = f"保存Excel模板完全失败: {str(final_err)}"
                    self.log_message(error_msg)
                    QMessageBox.critical(self, "保存失败", error_msg)
                    return
            
            self.log_message(f"Excel模板已保存到: {file_path}")
            QMessageBox.information(self, "下载成功", f"Excel模板已保存到: {file_path}\n\n特别提示：复合表达式中只需使用/HOST/作为占位符，系统会自动替换为实际的主机技术名称，无需手动替换。")
        except Exception as e:
            error_msg = f"下载Excel模板失败: {str(e)}"
            self.log_message(error_msg)
            
            # 提供更详细的错误信息
            if "Permission denied" in str(e):
                detailed_error = f"保存失败: 没有写入权限\n\n文件: {file_path}\n\n可能原因：\n1. 文件已被其他程序（如Excel）打开\n2. 您没有该位置的写入权限\n3. 文件被标记为只读\n\n建议：\n- 关闭所有Excel程序\n- 尝试保存到其他文件夹（如\"文档\"）\n- 以管理员身份运行程序"
                QMessageBox.critical(self, "权限错误", detailed_error)
            else:
                QMessageBox.critical(self, "下载失败", error_msg)
    
    def get_host_technical_name(self, host):
        """
        获取主机的技术名称
        
        Args:
            host (dict): 主机信息字典
            
        Returns:
            str: 主机的技术名称，如果无法获取则返回None
        """
        try:
            # 首先检查host字典是否已包含技术名称
            technical_name = host.get("host")
            if technical_name:
                return technical_name
            
            # 其次检查预先加载的映射表
            hostid = host.get("hostid")
            if hostid and hostid in self.host_info_map:
                complete_info = self.host_info_map[hostid]
                technical_name = complete_info.get("host")
                if technical_name:
                    self.log_message(f"从预加载的主机信息中获取技术名称: {technical_name}")
                    return technical_name
            
            # 再次检查名称到技术名称的映射
            visible_name = host.get("name")
            if visible_name and visible_name in self.name_to_host_map:
                technical_name = self.name_to_host_map[visible_name]
                self.log_message(f"从名称映射表获取技术名称: {visible_name} -> {technical_name}")
                return technical_name
            
            # 如果所有方法都失败，尝试进行实时API查询
            if visible_name or hostid:
                self.log_message(f"尝试实时查询主机 '{visible_name or hostid}' 的技术名称")
                
                try:
                    # 使用直接的JSON-RPC调用
                    if hasattr(self.api, 'auth') and hasattr(self.api, 'url'):
                        import requests
                        
                        # 如果有visible_name，先用它查询
                        if visible_name:
                            json_request = {
                                "jsonrpc": "2.0",
                                "method": "host.get",
                                "params": {
                                    "filter": {
                                        "name": [visible_name]
                                    },
                                    "output": ["host", "name", "hostid"]
                                },
                                "auth": self.api.auth,
                                "id": 0
                            }
                            
                            url = self.api.url
                            headers = {'Content-Type': 'application/json-rpc'}
                            r = requests.post(url, json=json_request, headers=headers)
                            response = r.json()
                            
                            if "result" in response and response["result"]:
                                host_data = response["result"][0]
                                technical_name = host_data.get("host")
                                if technical_name:
                                    # 更新映射表
                                    self.name_to_host_map[visible_name] = technical_name
                                    if "hostid" in host_data:
                                        self.host_info_map[host_data["hostid"]] = host_data
                                    self.log_message(f"通过API(name过滤)成功获取主机技术名称: {technical_name}")
                                    return technical_name
                        
                        # 如果有hostid，使用它查询
                        if hostid:
                            json_request = {
                                "jsonrpc": "2.0",
                                "method": "host.get",
                                "params": {
                                    "hostids": [hostid],
                                    "output": ["host", "name"]
                                },
                                "auth": self.api.auth,
                                "id": 0
                            }
                            
                            url = self.api.url
                            headers = {'Content-Type': 'application/json-rpc'}
                            r = requests.post(url, json=json_request, headers=headers)
                            response = r.json()
                            
                            if "result" in response and response["result"]:
                                host_data = response["result"][0]
                                technical_name = host_data.get("host")
                                if technical_name:
                                    # 更新映射表
                                    if "name" in host_data:
                                        self.name_to_host_map[host_data["name"]] = technical_name
                                    self.host_info_map[hostid] = host_data
                                    self.log_message(f"通过API(hostid过滤)成功获取主机技术名称: {technical_name}")
                                    return technical_name
                    
                    # 如果JSON-RPC调用失败，尝试使用do_request方法
                    elif hasattr(self.api, 'do_request'):
                        if visible_name:
                            response = self.api.do_request('host.get', {
                                'filter': {'name': [visible_name]},
                                'output': ["host", "name", "hostid"]
                            })
                            
                            if isinstance(response, dict) and "result" in response and response["result"]:
                                host_data = response["result"][0]
                                technical_name = host_data.get("host")
                                if technical_name:
                                    # 更新映射表
                                    self.name_to_host_map[visible_name] = technical_name
                                    if "hostid" in host_data:
                                        self.host_info_map[host_data["hostid"]] = host_data
                                    self.log_message(f"通过API(name过滤)成功获取主机技术名称: {technical_name}")
                                    return technical_name
                        
                        if hostid:
                            response = self.api.do_request('host.get', {
                                'hostids': [hostid],
                                'output': ["host", "name"]
                            })
                            
                            if isinstance(response, dict) and "result" in response and response["result"]:
                                host_data = response["result"][0]
                                technical_name = host_data.get("host")
                                if technical_name:
                                    # 更新映射表
                                    if "name" in host_data:
                                        self.name_to_host_map[host_data["name"]] = technical_name
                                    self.host_info_map[hostid] = host_data
                                    self.log_message(f"通过API(hostid过滤)成功获取主机技术名称: {technical_name}")
                                    return technical_name
                except Exception as api_err:
                    self.log_message(f"实时查询主机技术名称失败: {str(api_err)}")
            
            # 所有方法都失败
            self.log_message(f"错误：无法获取主机 '{visible_name or hostid or '未知主机'}' 的技术名称，该主机将被跳过")
            return None
            
        except Exception as e:
            self.log_message(f"获取主机技术名称时出错: {str(e)}")
            return None
    
    def get_selected_hosts(self):
        """获取表格中所有主机信息"""
        selected_hosts = []
        
        # 获取所有行
        total_rows = self.host_table.rowCount()
        logger.info(f"表格中共有 {total_rows} 行主机")
        
        for row in range(total_rows):
            try:
                # 获取主机ID
                hostid_item = self.host_table.item(row, 0)
                if not hostid_item:
                    continue
                    
                hostid = hostid_item.text()
                
                # 获取主机技术名称
                hostname_item = self.host_table.item(row, 2)
                hostname = hostname_item.text() if hostname_item else ""
                
                # 获取可见名称
                visible_name_item = self.host_table.item(row, 3)
                visible_name = visible_name_item.text() if visible_name_item else ""
                
                # 构建主机信息字典
                host_info = {
                    "hostid": hostid,
                    "host": hostname,
                    "name": visible_name
                }
                
                # 添加到主机列表
                selected_hosts.append(host_info)
                logger.info(f"添加主机: {host_info}")
                
            except Exception as e:
                logger.error(f"处理第{row}行主机信息时出错: {str(e)}")
                continue
        
        logger.info(f"共获取到 {len(selected_hosts)} 个主机")
        return selected_hosts
    
    def accept(self):
        """处理确认按钮点击事件，创建触发器"""
        try:
            # 验证必填字段
            trigger_name = self.name_edit.text().strip()
            
            # 检查触发器名称
            if not trigger_name:
                QMessageBox.warning(self, "验证失败", "触发器名称不能为空")
                return
                
            # 确定使用单一条件还是复合条件
            is_compound = self.compound_expr_radio.isChecked()
            
            # 初始化必要变量
            item_key = ""
            function_name = ""
            operator = ""
            threshold = ""
            param_value = ""
            is_count_param = False
            
            # 单一条件模式验证和数据准备
            if not is_compound:
                item_key = self.item_key_edit.text().strip()
                function_name = self.function_combo.currentText()
                operator = self.operator_combo.currentText()
                threshold = self.threshold_edit.text().strip()
                
                # 检查必填字段
                missing_fields = []
                if not item_key:
                    missing_fields.append("监控项键值")
                if not threshold and function_name != "nodata":
                    missing_fields.append("阈值")
                
                if missing_fields:
                    joined_fields = "\n- ".join(missing_fields)
                    QMessageBox.warning(
                        self, 
                        "验证失败", 
                        f"以下字段不能为空:\n- {joined_fields}"
                    )
                    return
                
                # 获取参数值和类型
                is_count_param = self.count_param_radio.isChecked()
                
                if is_count_param:
                    count_value = self.count_param_edit.text().strip()
                    if count_value:
                        # 确保数据点数量是整数
                        try:
                            count_value = float(count_value)
                            count_value = str(int(count_value))  # 转换为整数
                            self.log_message(f"数据点数量转换为整数: {count_value}")
                        except (ValueError, TypeError):
                            pass
                        param_value = f"#{count_value}"
                else:
                    time_value = self.time_param_edit.text().strip()
                    if time_value:
                        param_value = time_value
                        # 确保时间参数有单位
                        if not any(unit in param_value for unit in ["s", "m", "h", "d"]):
                            param_value = f"{param_value}s"  # 默认添加秒单位
                
                # 检查参数是否符合函数要求
                if function_name in self.function_types["data_only"] and not is_count_param and param_value:
                    QMessageBox.critical(
                        self, 
                        "参数类型错误", 
                        f"函数 {function_name} 只支持数据点数量参数(#n)，不支持时间范围参数。\n请返回并切换到数据点数量模式。"
                    )
                    return
                    
                if function_name in self.function_types["time_only"] and is_count_param:
                    QMessageBox.critical(
                        self, 
                        "参数类型错误", 
                        f"函数 {function_name} 只支持时间范围参数，不支持数据点数量参数。\n请返回并切换到时间范围模式。"
                    )
                    return
            # 复合条件模式验证
            else:
                if not self.expressions:
                    QMessageBox.warning(
                        self, 
                        "验证失败", 
                        "请至少添加一个条件"
                    )
                    return
                self.log_message("使用复合条件创建触发器")
            
            # 基本信息准备 - 适用于单一条件和复合条件
            description = self.description_edit.text() or trigger_name
            priority = self.severity_combo.currentIndex()
            recovery_mode = self.recovery_mode_combo.currentIndex()
            
            # 获取手动关闭状态
            manual_close = "1" if self.manual_close_checkbox.isChecked() else "0"
            
            # 复合条件不支持恢复表达式
            if is_compound and recovery_mode == 1:  # 使用恢复表达式
                self.log_message("警告：复合条件触发器不支持指定恢复表达式，将使用问题表达式作为恢复条件")
                recovery_mode = 0  # 切换到使用问题表达式
            
            # 日志主机和参数信息，帮助调试
            self.log_message(f"触发器参数: function={function_name}, param_value={param_value}, is_count_param={is_count_param}")
            if self.hosts:
                for i, host in enumerate(self.hosts):
                    self.log_message(f"主机 {i+1}: ID={host.get('hostid', '?')}, host={host.get('host', '?')}, name={host.get('name', '?')}")
            
            # 获取恢复表达式参数 - 先在循环外获取通用设置
            recovery_key = ""
            recovery_function_name = ""
            recovery_operator = ""
            recovery_threshold = ""
            recovery_param = ""
            recovery_is_count_param = False
            
            if recovery_mode == 1:  # 使用恢复表达式
                recovery_key = self.recovery_key_edit.text().strip() or item_key
                
                # 修复恢复键值中可能存在的格式问题
                if "[," in recovery_key:
                    # 修复像 "system.cpu.util[,user]" 这样的格式
                    recovery_key = recovery_key.replace("[,", "[*,")
                    self.log_message(f"修复恢复键值格式，添加缺失的参数: {recovery_key}")
                
                recovery_function_name = self.recovery_function_combo.currentText()
                recovery_operator = self.recovery_operator_combo.currentText()
                
                # 确保恢复阈值是字符串格式，避免浮点数的科学记数法表示
                recovery_threshold_text = self.recovery_threshold_edit.text().strip()
                try:
                    recovery_threshold_value = float(recovery_threshold_text)
                    # 检查是否是整数
                    if recovery_threshold_value == int(recovery_threshold_value):
                        recovery_threshold = str(int(recovery_threshold_value))
                    else:
                        recovery_threshold = str(recovery_threshold_value)
                except (ValueError, TypeError):
                    recovery_threshold = recovery_threshold_text
                
                # 更详细地记录恢复参数设置
                self.log_message("===== 恢复表达式参数设置 =====")
                self.log_message(f"恢复键值: {recovery_key}")
                self.log_message(f"恢复函数: {recovery_function_name}")
                self.log_message(f"恢复操作符: {recovery_operator}")
                self.log_message(f"恢复阈值: {recovery_threshold}")
                
                # 处理恢复参数
                recovery_is_count_param = self.recovery_count_param_radio.isChecked()
                inherit_params = self.recovery_param_inherit_check.isChecked()
                
                if inherit_params:
                    # 继承主表达式的参数
                    recovery_param = param_value
                    recovery_is_count_param = is_count_param
                    self.log_message(f"恢复表达式继承主表达式参数: {recovery_param}, is_count={recovery_is_count_param}")
                else:
                    if recovery_is_count_param:
                        count_value = self.recovery_count_param_edit.text().strip()
                        if count_value:
                            try:
                                count_value = float(count_value)
                                count_value = str(int(count_value))  # 转换为整数
                            except (ValueError, TypeError):
                                pass
                            recovery_param = f"#{count_value}"
                    else:
                        time_value = self.recovery_time_param_edit.text().strip()
                        if time_value:
                            recovery_param = time_value
                            # 确保时间参数有单位
                            if not any(unit in recovery_param for unit in ["s", "m", "h", "d"]):
                                recovery_param = f"{recovery_param}s"  # 默认添加秒单位
                
                self.log_message(f"恢复参数: {recovery_param}, is_count={recovery_is_count_param}")
                
                # 验证恢复参数与恢复函数的兼容性
                if recovery_mode == 1 and recovery_function_name in self.function_types["data_only"] and recovery_param:
                    if not recovery_is_count_param and not recovery_param.startswith("#"):
                        QMessageBox.critical(
                            self, 
                            "参数类型错误", 
                            f"恢复函数 {recovery_function_name} 只支持数据点数量参数(#n)，不支持时间范围参数。\n请返回并切换到数据点数量模式。"
                        )
                        return
            
            # 开始为每个选中的主机创建触发器
            created_count = 0
            error_count = 0
            
            for host in self.hosts:
                # 获取主机技术名称
                host_technical_name = self.get_host_technical_name(host)
                if not host_technical_name:
                    self.log_message(f"警告: 无法获取主机ID={host.get('hostid', '?')}的技术名称，跳过")
                    error_count += 1
                    continue
                
                try:
                    # 构建表达式
                    expression = ""
                    recovery_expression = ""
                    
                    if is_compound:
                        # 使用复合表达式
                        if not self.expressions:
                            self.log_message(f"警告: 复合条件列表为空，跳过主机 {host_technical_name}")
                            error_count += 1
                            continue
                        
                        # 获取复合表达式逻辑运算符
                        logical_operator = "and" if self.and_operator_radio.isChecked() else "or"
                        
                        # 根据每个条件构建表达式
                        sub_expressions = []
                        for expr in self.expressions:
                            sub_expr = self.build_single_expression(expr, host_technical_name)
                            if sub_expr:
                                sub_expressions.append(sub_expr)
                        
                        if not sub_expressions:
                            self.log_message(f"警告: 无法构建有效的子表达式，跳过主机 {host_technical_name}")
                            error_count += 1
                            continue
                        
                        # 组合表达式
                        expression = f" {logical_operator} ".join(sub_expressions)
                    else:
                        # 使用单一条件表达式
                        # 检查阈值格式 - 如果阈值本身包含小数点，需要使用引号
                        if function_name != "nodata":
                            if "." in threshold:
                                # 检查是否已经有引号
                                if not (threshold.startswith('"') and threshold.endswith('"')):
                                    # 尝试将阈值解析为数字
                                    try:
                                        float(threshold)
                                        # 是有效的数字，不需要添加引号
                                    except ValueError:
                                        # 不是有效的数字，需要添加引号
                                        threshold = f'"{threshold}"'
                            elif not threshold.isdigit() and not (threshold.startswith('"') and threshold.endswith('"')):
                                # 非数字也需要添加引号
                                threshold = f'"{threshold}"'
                        
                        # 对于nodata函数，使用特殊格式
                        if function_name == "nodata":
                            time_param = param_value if param_value else "5m"
                            # 确保时间参数有单位
                            if not any(unit in time_param for unit in ["s", "m", "h", "d"]):
                                time_param = f"{time_param}m"  # 默认添加分钟单位
                            expression = f"{function_name}(/{host_technical_name}/{item_key},{time_param})=1"
                        else:
                            # 一般格式的函数
                            if param_value:
                                expression = f"{function_name}(/{host_technical_name}/{item_key},{param_value}){operator}{threshold}"
                            else:
                                expression = f"{function_name}(/{host_technical_name}/{item_key}){operator}{threshold}"
                    
                    # 构建恢复表达式
                    if recovery_mode == 1:  # 使用恢复表达式
                        if recovery_function_name == "nodata":
                            recovery_time_param = recovery_param if recovery_param else "5m"
                            # 确保时间参数有单位
                            if not any(unit in recovery_time_param for unit in ["s", "m", "h", "d"]):
                                recovery_time_param = f"{recovery_time_param}m"  # 默认添加分钟单位
                            recovery_expression = f"{recovery_function_name}(/{host_technical_name}/{recovery_key},{recovery_time_param})=1"
                        else:
                            if recovery_param:
                                recovery_expression = f"{recovery_function_name}(/{host_technical_name}/{recovery_key},{recovery_param}){recovery_operator}{recovery_threshold}"
                            else:
                                recovery_expression = f"{recovery_function_name}(/{host_technical_name}/{recovery_key}){recovery_operator}{recovery_threshold}"
                    
                    # 记录表达式和恢复表达式
                    self.log_message(f"主机: {host_technical_name}")
                    self.log_message(f"表达式: {expression}")
                    if recovery_expression:
                        self.log_message(f"恢复表达式: {recovery_expression}")
            
                    # 获取禁用状态
                    status = "1" if self.status_checkbox.isChecked() else "0"
                    
                    # 构建触发器参数
                    trigger_params = {
                        "description": trigger_name,
                        "expression": expression,
                        "priority": str(priority),
                        "comments": description,
                        "status": status,
                        "manual_close": manual_close
                    }
                    
                    # 设置恢复模式和恢复表达式
                    trigger_params["recovery_mode"] = str(recovery_mode)
                    if recovery_mode == 1 and recovery_expression:  # 使用恢复表达式
                        trigger_params["recovery_expression"] = recovery_expression
                    
                    # 记录完整的触发器参数以进行调试
                    self.log_message(f"完整的触发器参数: {trigger_params}")
                    
                    # 创建触发器
                    result = self.api._call("trigger.create", [trigger_params])
                    if result and "triggerids" in result:
                        self.log_message(f"成功创建触发器 ID: {result['triggerids'][0]}")
                        created_count += 1
                    else:
                        self.log_message(f"创建触发器失败，未返回预期结果: {result}")
                        error_count += 1
                except Exception as api_err:
                    self.log_message(f"API调用失败: {str(api_err)}")
                    error_count += 1
                
                except Exception as e:
                    self.log_message(f"处理主机 {host_technical_name} 时发生错误: {str(e)}")
                    error_count += 1
            
            # 显示结果
            if created_count > 0:
                QMessageBox.information(
                    self,
                    "创建成功",
                    f"成功创建 {created_count} 个触发器" +
                    (f"\n{error_count} 个失败" if error_count > 0 else "")
                )
                # 关闭对话框
                super().accept()
            else:
                QMessageBox.critical(
                    self,
                    "创建失败",
                    f"创建触发器失败\n{error_count} 个错误发生"
                )
        except Exception as e:
            self.log_message(f"创建触发器过程中发生错误: {str(e)}")
            QMessageBox.critical(
                self,
                "错误",
                f"创建触发器过程中发生错误: {str(e)}"
            )

    def on_function_changed(self, index):
        """处理函数选择变化"""
        selected_function = self.function_combo.currentText()
        self.log_message(f"选择函数: {selected_function}")
        
        # 根据函数类型设置参数类型的可用性
        if selected_function in self.function_types["data_only"]:
            # 只支持数据点数量
            self.count_param_radio.setChecked(True)
            self.time_param_radio.setEnabled(False)
            self.count_param_radio.setEnabled(True)
            self.log_message(f"函数 {selected_function} 只支持数据点数量参数，已自动调整")
        elif selected_function in self.function_types["time_only"]:
            # 只支持时间范围
            self.time_param_radio.setChecked(True)
            self.time_param_radio.setEnabled(True)
            self.count_param_radio.setEnabled(False)
            self.log_message(f"函数 {selected_function} 只支持时间范围参数，已自动调整")
        else:
            # 两种参数都支持
            self.time_param_radio.setEnabled(True)
            self.count_param_radio.setEnabled(True)
            
        # 根据当前选择更新可见性
        self.on_param_type_changed(True)

    def on_recovery_function_changed(self, index):
        """处理恢复函数选择变化"""
        selected_function = self.recovery_function_combo.currentText()
        self.log_message(f"选择恢复函数: {selected_function}")
        
        # 检查是否需要继承问题表达式参数
        is_inherit = hasattr(self, 'recovery_param_inherit_check') and self.recovery_param_inherit_check.isChecked()
        
        if is_inherit:
            # 如果继承问题表达式参数，则根据问题表达式的参数类型设置
            # 禁用参数类型选择，因为会使用问题表达式的参数类型
            self.recovery_time_param_radio.setEnabled(False)
            self.recovery_count_param_radio.setEnabled(False)
            
            # 设置与问题表达式相同的参数类型
            if self.count_param_radio.isChecked():
                self.recovery_count_param_radio.setChecked(True)
                self.recovery_time_param_edit.setVisible(False)
                self.recovery_count_param_edit.setVisible(True)
            else:
                self.recovery_time_param_radio.setChecked(True)
                self.recovery_time_param_edit.setVisible(True)
                self.recovery_count_param_edit.setVisible(False)
        else:
            # 不继承参数时，根据当前选择的恢复函数自动设置正确的参数类型
            self.log_message(f"取消继承参数，当前恢复函数: {selected_function}")
            if selected_function in self.function_types["data_only"]:
                # 只支持数据点数量
                self.recovery_count_param_radio.setChecked(True)
                self.recovery_time_param_radio.setEnabled(False)
                self.recovery_count_param_radio.setEnabled(True)
                self.recovery_time_param_edit.setVisible(False)
                self.recovery_count_param_edit.setVisible(True)
                self.log_message(f"函数 {selected_function} 只支持数据点数量参数，已自动调整")
            elif selected_function in self.function_types["time_only"]:
                # 只支持时间范围
                self.recovery_time_param_radio.setChecked(True)
                self.recovery_time_param_radio.setEnabled(True)
                self.recovery_count_param_radio.setEnabled(False)
                self.recovery_time_param_edit.setVisible(True)
                self.recovery_count_param_edit.setVisible(False)
                self.log_message(f"函数 {selected_function} 只支持时间范围参数，已自动调整")
            else:
                # 两种参数都支持，保持当前选择
                self.recovery_time_param_radio.setEnabled(True)
                self.recovery_count_param_radio.setEnabled(True)
                # 根据当前选择更新可见性
                self.on_recovery_param_type_changed(True)
                
    def on_param_type_changed(self, checked):
        """处理参数类型变化"""
        is_time_param = self.time_param_radio.isChecked()
        self.time_param_edit.setVisible(is_time_param)
        self.count_param_edit.setVisible(not is_time_param)

    def on_recovery_param_type_changed(self, checked):
        """处理恢复参数类型变化"""
        is_time_param = self.recovery_time_param_radio.isChecked()
        self.recovery_time_param_edit.setVisible(is_time_param)
        self.recovery_count_param_edit.setVisible(not is_time_param)
    
    def on_recovery_mode_changed(self, index):
        """处理恢复模式变化"""
        # 根据选择的恢复模式显示/隐藏恢复表达式设置
        if index == 1:  # 使用恢复表达式
            self.recovery_form.setVisible(True)
        else:
            self.recovery_form.setVisible(False)
    
    def on_recovery_param_inherit_changed(self, checked):
        """处理恢复参数继承选项变化"""
        if checked:
            # 继承问题表达式参数，禁用恢复表达式参数设置
            self.recovery_time_param_radio.setEnabled(False)
            self.recovery_count_param_radio.setEnabled(False)
            self.recovery_time_param_edit.setEnabled(False)
            self.recovery_count_param_edit.setEnabled(False)
            
            # 设置与问题表达式相同的参数类型
            if self.count_param_radio.isChecked():
                self.recovery_count_param_radio.setChecked(True)
                self.recovery_time_param_edit.setVisible(False)
                self.recovery_count_param_edit.setVisible(True)
            else:
                self.recovery_time_param_radio.setChecked(True)
                self.recovery_time_param_edit.setVisible(True)
                self.recovery_count_param_edit.setVisible(False)
        else:
            # 不继承，启用恢复表达式参数设置
            # 根据所选函数类型设置参数类型的可用性
            selected_function = self.recovery_function_combo.currentText()
            
            if selected_function in self.function_types["data_only"]:
                # 只支持数据点数量
                self.recovery_count_param_radio.setChecked(True)
                self.recovery_time_param_radio.setEnabled(False)
                self.recovery_count_param_radio.setEnabled(True)
            elif selected_function in self.function_types["time_only"]:
                # 只支持时间范围
                self.recovery_time_param_radio.setChecked(True)
                self.recovery_time_param_radio.setEnabled(True)
                self.recovery_count_param_radio.setEnabled(False)
            else:
                # 两种参数都支持
                self.recovery_time_param_radio.setEnabled(True)
                self.recovery_count_param_radio.setEnabled(True)
            
            # 启用参数输入
            self.recovery_time_param_edit.setEnabled(True)
            self.recovery_count_param_edit.setEnabled(True)
            
            # 根据当前选择更新可见性
            self.on_recovery_param_type_changed(True)

    def on_preview_expr(self):
        """生成表达式预览"""
        if not self.item_key_edit.text():
            QMessageBox.warning(self, "验证失败", "请输入监控项键值")
            return
        
        # 获取表单数据
        trigger_name = self.name_edit.text().strip()
        item_key = self.item_key_edit.text().strip()
        
        # 修复键值中可能存在的格式问题
        if "[," in item_key:
            # 修复像 "system.cpu.util[,user]" 这样的格式
            item_key = item_key.replace("[,", "[*,")
            self.log_message(f"修复键值格式，添加缺失的参数: {item_key}")
            
        function_name = self.function_combo.currentText()
        operator = self.operator_combo.currentText()
        
        # 确保阈值是字符串格式，避免浮点数的科学记数法表示
        threshold_text = self.threshold_edit.text().strip()
        try:
            threshold_value = float(threshold_text)
            # 检查是否是整数
            if threshold_value == int(threshold_value):
                threshold = str(int(threshold_value))
            else:
                threshold = str(threshold_value)
        except (ValueError, TypeError):
            threshold = threshold_text
        
        # 获取参数值和类型
        param_value = ""
        is_count_param = self.count_param_radio.isChecked()
        
        if is_count_param:
            count_value = self.count_param_edit.text().strip()
            if count_value:
                # 确保数据点数量是整数
                try:
                    count_value = float(count_value)
                    count_value = str(int(count_value))  # 转换为整数
                    self.log_message(f"数据点数量转换为整数: {count_value}")
                except (ValueError, TypeError):
                    pass
                param_value = f"#{count_value}"
        else:
            time_value = self.time_param_edit.text().strip()
            if time_value:
                param_value = time_value
                # 确保时间参数有单位
                if not any(unit in param_value for unit in ["s", "m", "h", "d"]):
                    param_value = f"{param_value}s"  # 默认添加秒单位
        
        # 检查参数是否符合函数要求
        if function_name in self.function_types["data_only"] and not is_count_param and param_value:
            QMessageBox.warning(
                self, 
                "参数类型错误", 
                f"函数 {function_name} 只支持数据点数量参数(#n)，不支持时间范围参数。\n请切换到数据点数量模式。"
            )
            return
        
        if function_name in self.function_types["time_only"] and is_count_param:
            QMessageBox.warning(
                self, 
                "参数类型错误", 
                f"函数 {function_name} 只支持时间范围参数，不支持数据点数量参数。\n请切换到时间范围模式。"
            )
            return
        
        # 选择第一台主机作为示例生成表达式
        if self.hosts and len(self.hosts) > 0:
            host = self.hosts[0]
            # 获取主机名 - 重要修复：始终使用主机的技术名称(host)来构建触发器表达式
            host_technical_name = self.get_host_technical_name(host)
            
            if not host_technical_name:
                # 如果确实无法获取主机名，显示警告
                QMessageBox.warning(self, "预览失败", "无法获取主机名，请确保主机信息完整")
                return
            
            # 获取可见名称用于显示
            visible_name = host.get("name", host_technical_name)
            self.log_message(f"为主机 '{visible_name}' 创建表达式预览，使用技术名称 '{host_technical_name}' 构建表达式")
            
            # 构建表达式
            expr = self.build_single_expression({
                "item_key": item_key,
                "function": function_name,
                "operator": operator,
                "threshold": threshold,
                "param": param_value,
                "is_count_param": is_count_param
            }, host_technical_name)
            
            # 显示表达式
            self.expr_preview.setText(expr)
            self.log_message(f"生成的表达式预览: {expr}")
        else:
            QMessageBox.warning(self, "预览失败", "没有选择任何主机")
            return 